﻿#if UNITY_2019_4_OR_NEWER
#define UNITY_3D
#endif
#if BURST_COMPILE
using Unity.Burst;
#endif
#if UNITY_3D
using UnityEngine;
#endif

namespace Devil
{
    public enum EIntersection : byte
    {
        None,
        Outside,
        Inside,
    }

#if BURST_COMPILE
    [BurstCompile]
#endif
    public static class MathExt
    {
        public const float CUBE_EXTEND_SIZE = 1.73205080756f;

#if BURST_COMPILE && !UNITY_EDITOR
        [BurstCompile]
#endif
        public static float Max(float a, float b, float c)
        {
            return a > b ? (a > c ? a : c) : (b > c ? b : c);
        }

#if BURST_COMPILE && !UNITY_EDITOR
        [BurstCompile]
#endif
        public static int Max(int a, int b, int c)
        {
            return a > b ? (a > c ? a : c) : (b > c ? b : c);
        }

#if BURST_COMPILE && !UNITY_EDITOR
        [BurstCompile]
#endif
        public static float Min(float a, float b, float c)
        {
            return a > b ? (b > c ? c : b) : (a > c ? c : a);
        }

#if BURST_COMPILE && !UNITY_EDITOR
        [BurstCompile]
#endif
        public static int Min(int a, int b, int c)
        {
            return a > b ? (b > c ? c : b) : (a > c ? c : a);
        }

#if BURST_COMPILE && !UNITY_EDITOR
        [BurstCompile]
#endif
        public static float Pow(float b, int times)
        {
            if (b == 0)
                return 0;
            float t = 1;
            for (int i = 0; i < times; i++)
            {
                t *= b;
            }
            return t;
        }

#if BURST_COMPILE && !UNITY_EDITOR
        [BurstCompile]
#endif
        public static int Hash(int a, int b)
        {
            return (a << 5) - a + b;
        }

#if BURST_COMPILE && !UNITY_EDITOR
        [BurstCompile]
#endif
        public static int Hash(int a, int b, int c)
        {
            var v = (a << 5) - a + b;
            return (v << 5) - v + c;
        }

#if BURST_COMPILE && !UNITY_EDITOR
        [BurstCompile]
#endif
        public static int Hash(int a, int b, int c, int d)
        {
            var v = (a << 5) - a + b;
            v = (v << 5) - v + c;
            v = (v << 5) - v + d;
            return v;
        }

#if BURST_COMPILE && !UNITY_EDITOR
        [BurstCompile]
#endif
        public static int GetBitIndex(uint value)
        {
            if (value == 0)
                return -1;
            int l = 0;
            int r = 32;
            int n = 16;
            uint v = value >> n;
            while (v != 1)
            {
                if (v == 0)
                {
                    r = n - 1;
                }
                else
                {
                    l = n + 1;
                }
                n = (l + r) >> 1;
                v = value >> n;
            }
            return n;
        }

#if UNITY_3D

#if BURST_COMPILE && !UNITY_EDITOR
        [BurstCompile]
#endif
        public static void DecodeRGB(int value, out Color c)
        {
            c.r = ((value & 0xff0000) >> 16) / 255f;
            c.g = ((value & 0xff00) >> 8) / 255f;
            c.b = ((value & 0xff)) / 255f;
            c.a = 1;
        }

        public static Color DecodeRGB(int value)
        {
            Color c;
            DecodeRGB(value, out c);
            return c;
        }

#if BURST_COMPILE && !UNITY_EDITOR
        [BurstCompile]
#endif
        public static float Max(in Vector3 vec)
        {
            return Max(vec.x, vec.y, vec.z);
        }

#if BURST_COMPILE && !UNITY_EDITOR
        [BurstCompile]
#endif
        public static long Pow(long b, int times)
        {
            if (b == 0)
                return 0;
            long v = 1;
            for (int i = 0; i < times; i++)
            {
                v *= b;
            }
            return v;
        }

#if BURST_COMPILE && !UNITY_EDITOR
        [BurstCompile]
#endif
        public static int RoundToInt(float value, float threshold)
        {
            var v = Mathf.FloorToInt(value);
            if (value - v > threshold)
                return v + 1;
            return v;
        }

        public static Vector3 Mul(Vector3 a, Vector3 b)
        {
            return new Vector3(a.x * b.x, a.y * b.y, a.z * b.z);
        }

        public static Vector3 Div(Vector3 a, Vector3 b)
        {
            return new Vector3(a.x / b.x, a.y / b.y, a.z / b.z);
        }

        public static Vector3 Bezier3(float t, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3)
        {
            t = Mathf.Clamp01(t);
            float d = 1f - t;
            return d * d * d * p0 + 3f * d * d * t * p1
                + 3f * d * t * t * p2 + t * t * t * p3;
        }

        public static Vector3 BezierTangent3(float t, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3)
        {
            t = Mathf.Clamp01(t);
            return (-3f * p0 + 9f * p1 - 9f * p2 + 3f * p3) * (t * t)
                + (6f * p0 - 12f * p1 + 6f * p2) * t
                - 3f * p0 + 3f * p1;
        }

        public static float Bezier1(float t, float p0, float p1, float p2, float p3)
        {
            t = Mathf.Clamp01(t);
            float d = 1f - t;
            return d * d * d * p0 + 3f * d * d * t * p1
                + 3f * d * t * t * p2 + t * t * t * p3;
        }

        public static float BezierTangent1(float t, float p0, float p1, float p2, float p3)
        {
            t = Mathf.Clamp01(t);
            return (-3f * p0 + 9f * p1 - 9f * p2 + 3f * p3) * t * t
                + (6f * p0 - 12f * p1 + 6f * p2) * t
                - 3f * p0 + 3f * p1;
        }

        public static bool IsVaild(Vector3 pos)
        {
            return !(float.IsNaN(pos.x) || float.IsInfinity(pos.x) || float.IsNaN(pos.y) || float.IsInfinity(pos.y) || float.IsNaN(pos.z) || float.IsInfinity(pos.z));
        }


#if BURST_COMPILE && !UNITY_EDITOR
        [BurstCompile]
#endif
        public static float CalculateVolume(in Bounds bd)
        {
            var size = bd.size;
            return size.x * size.y * size.z;
        }

        /// <summary>
        /// 计算以normal为发现确定的平面上，从from旋转到to的旋转方向
        /// </summary>
        /// <param name="from"></param>
        /// <param name="to"></param>
        /// <param name="normal"></param>
        /// <returns></returns>
#if BURST_COMPILE && !UNITY_EDITOR
        [BurstCompile]
#endif
        public static float RotateDirection(in Vector3 from, in Vector3 to, in Vector3 normal)
        {
            Vector3 projFrom = Vector3.ProjectOnPlane(from, normal);
            Vector3 projTo = Vector3.ProjectOnPlane(to, normal);
            Vector3 cross = Vector3.Cross(projFrom, projTo);
            float dir = Vector3.Dot(cross, normal);
            return Mathf.Sign(dir);
        }

#if BURST_COMPILE && !UNITY_EDITOR
        [BurstCompile]
#endif
        public static float RotateDirectionOnPlane(in Vector3 from, in Vector3 to, in Vector3 normal)
        {
            var cross = Vector3.Cross(from, to);
            return Mathf.Sign(Vector3.Dot(cross, normal));
        }

        /// <summary>
        /// 计算以normal为法线确定的平面上，从from旋转到to的旋转角度
        /// </summary>
        /// <param name="from"></param>
        /// <param name="to"></param>
        /// <param name="normal"></param>
        /// <returns></returns>
#if BURST_COMPILE && !UNITY_EDITOR
        [BurstCompile]
#endif
        public static float RotateAngleFromTo(in Vector3 from, in Vector3 to, in Vector3 normal)
        {
            Vector3 projFrom = Vector3.ProjectOnPlane(from, normal);
            Vector3 projTo = Vector3.ProjectOnPlane(to, normal);
            Vector3 cross = Vector3.Cross(projFrom, projTo);
            float dir = Vector3.Dot(cross, normal);
            return Mathf.Sign(dir) * Vector3.Angle(projFrom, projTo);
        }

#if BURST_COMPILE && !UNITY_EDITOR
        [BurstCompile]
#endif
        public static float FreeRotateAngleFromTo(in Vector3 from, in Vector3 to, in Vector3 normal)
        {
            var cross = Vector3.Cross(from, to);
            var dir = Vector3.Dot(cross, normal);
            return Mathf.Sign(dir) * Vector3.Angle(from, to);
        }

#if BURST_COMPILE && !UNITY_EDITOR
        [BurstCompile]
#endif
        public static void RotateFromTo(in Quaternion from, in Quaternion to, out Quaternion rot)
        {
            var r = from;
            r.x = -r.x;
            r.y = -r.y;
            r.z = -r.z;
            rot = to * r;
        }

        public static Quaternion RotateTo(this Quaternion rot, Quaternion to)
        {
            Quaternion r;
            RotateFromTo(rot, to, out r);
            return r;
        }

        /// <summary>
        /// 反向旋转
        /// </summary>
        /// <param name="rot"></param>
        /// <returns></returns>
        public static Quaternion Inverse(this Quaternion rot)
        {
            return new Quaternion(-rot.x, -rot.y, -rot.z, rot.w);
        }

        /// <summary>
        /// 到平面距离
        /// </summary>
        /// <param name="p"></param>
        /// <param name="pointInPlane"></param>
        /// <param name="planeNormal"></param>
        /// <returns>正面：正数，反面：负数 </returns>
#if BURST_COMPILE && !UNITY_EDITOR
        [BurstCompile]
#endif
        public static float DistanceToPlane(in Vector3 p, in Vector3 pointInPlane, in Vector3 planeNormal)
        {
            var dir = p - pointInPlane;
            return Vector3.Dot(dir, planeNormal.normalized);
        }

        /// <summary>
        /// 计算点到线段的距离, 如果垂线交点在 (from,to) 之外，将返回 (from,p) 或 (to,p) 的距离
        /// </summary>
        /// <param name="p"></param>
        /// <param name="from"></param>
        /// <param name="to"></param>
        /// <returns></returns>
#if BURST_COMPILE && !UNITY_EDITOR
        [BurstCompile]
#endif
        public static float DistanceToLine(in Vector3 p, in Vector3 from, in Vector3 to)
        {
            var ap = p - from;
            var dir = (to - from).normalized;
            var proj = dir * Vector3.Dot(ap, dir);
            if (Vector3.Dot(proj, dir) <= 0)
                return Vector3.Distance(p, from);
            else if (Vector3.Dot(from + proj - to, -dir) <= 0)
                return Vector3.Distance(p, to);
            else
                return Vector3.Distance(from + proj, p);
        }

        /// <summary>
        /// 判断点p在平面前面否
        /// </summary>
        /// <param name="p"></param>
        /// <param name="pointInPlane"></param>
        /// <param name="planeNormal"></param>
        /// <returns></returns>
#if BURST_COMPILE && !UNITY_EDITOR
        [BurstCompile]
#endif
        public static bool IsFront(in Vector3 p, in Vector3 pointInPlane, in Vector3 planeNormal)
        {
            var dir = p - pointInPlane;
            return Vector3.Dot(dir, planeNormal) >= 0;
        }

#if BURST_COMPILE && !UNITY_EDITOR
        [BurstCompile]
#endif
        public static bool IsBack(in Vector3 p, in Vector3 pointInPlane, in Vector3 planeNormal)
        {
            var dir = p - pointInPlane;
            return Vector3.Dot(dir, planeNormal) < 0;
        }

        // not normalized
        public static Vector3 TopNormalDir(in FrustumPlanes frustum)
        {
            return new Vector3(0,
                frustum.zFar * frustum.right - frustum.zFar * frustum.left,
                frustum.left * frustum.top - frustum.right * frustum.top);
        }

        public static Vector3 DownNormalDir(in FrustumPlanes frustum)
        {
            return new Vector3(0,
                frustum.zFar * frustum.left - frustum.zFar * frustum.right,
                frustum.right * frustum.bottom - frustum.left * frustum.bottom);
        }

        public static Vector3 RightNormalDir(in FrustumPlanes frustum)
        {
            return new Vector3(frustum.zFar * frustum.top - frustum.zFar * frustum.bottom,
                0,
                frustum.right * frustum.bottom - frustum.right * frustum.top);
        }

        public static Vector3 LeftNormalDir(in FrustumPlanes frustum)
        {
            return new Vector3(frustum.zFar * frustum.bottom - frustum.zFar * frustum.top,
                0,
                frustum.left * frustum.top - frustum.left * frustum.bottom);
        }

#if BURST_COMPILE && !UNITY_EDITOR
        [BurstCompile]
#endif
        public static bool IsInFrustum(in Vector3 p, in FrustumPlanes frustum)
        {
            if (p.z <= frustum.zNear || p.z >= frustum.zFar)
                return false;
            var nor = TopNormalDir(frustum);
            if (Vector3.Dot(p, nor) >= 0)
                return false;
            nor = DownNormalDir(frustum);
            if (Vector3.Dot(p, nor) >= 0)
                return false;
            nor = RightNormalDir(frustum);
            if (Vector3.Dot(p, nor) >= 0)
                return false;
            nor = LeftNormalDir(frustum);
            if (Vector3.Dot(p, nor) >= 0)
                return false;
            return true;
        }

#if BURST_COMPILE && !UNITY_EDITOR
        [BurstCompile]
#endif
        public static bool IsInFrustum(in Vector3 p, float zFar, float zNear, float x, float y)
        {
            if (p.z <= zNear || p.z >= zFar)
                return false;
            var nor = new Vector3(0,
                zFar * x * 2,
                -2 * x * y);
            if (Vector3.Dot(p, nor) >= 0)
                return false;
            nor = new Vector3(0,
                -2 * zFar * x,
                -2 * x * y);
            if (Vector3.Dot(p, nor) >= 0)
                return false;
            nor = new Vector3(zFar * y * 2,
                0,
               -2 * x * y);
            if (Vector3.Dot(p, nor) >= 0)
                return false;
            nor = new Vector3(-2 * zFar * y,
                0,
                -2 * x * y);
            if (Vector3.Dot(p, nor) >= 0)
                return false;
            return true;
        }

#if BURST_COMPILE && !UNITY_EDITOR
        [BurstCompile]
#endif
        public static bool IsInFrustum(in Bounds bounds, float zFar, float zNear, float x, float y)
        {
            var min = bounds.min;
            var max = bounds.max;
            if (min.z > zFar || max.z < zNear || min.x > x || max.x < -x || min.y > y || max.y < -y)
                return false;
            var z = Mathf.Min(max.z, zFar);
            var fac = z / zFar;
            x *= fac;
            y *= fac;
            return !(min.x > x || max.x < -x || min.y > y || max.y < -y);
        }

        /// <summary>
        /// 计算射线与平面的交点
        /// </summary>
        /// <param name="original"></param>
        /// <param name="direction"></param>
        /// <param name="anyPointInPlane"></param>
        /// <param name="planeNormal"></param>
        /// <param name="point"></param>
        /// <returns></returns>
#if BURST_COMPILE && !UNITY_EDITOR
        [BurstCompile]
#endif
        public static EIntersection CalculateIntersectionPoint(in Vector3 original, in Vector3 direction, in Vector3 anyPointInPlane, in Vector3 planeNormal, out Vector3 point)
        {
            point = anyPointInPlane;
            var normal = planeNormal.normalized;
            float dot2 = Vector3.Dot(direction, normal);
            if (dot2 == 0)
            {
                //hasPoint = false;
                return EIntersection.None;
            }
            if (dot2 < 0)
            {
                normal = -planeNormal;
                dot2 = -dot2;
            }
            Vector3 p = anyPointInPlane - original;
            float dot1 = Vector3.Dot(normal, p);
            if (dot1 == 0)
            {
                point = original;
                //hasPoint = true;
                return EIntersection.Inside;
            }
            Vector3 oo1 = dot1 * normal;
            Vector3 oo2 = dot2 * normal;
            point = original + oo1 + (dot1 / dot2) * (direction - oo2);
            return Vector3.Dot(point - original, direction) >= 0 ? EIntersection.Inside : EIntersection.Outside;

        }

        public static Vector3 CalculateIntersectionPoint(in Vector3 original, in Vector3 direction, in Vector3 anyPointInPlane, in Vector3 planeNormal)
        {
            Vector3 p;
            CalculateIntersectionPoint(original, direction, anyPointInPlane, planeNormal, out p);
            return p;
        }

#if BURST_COMPILE && !UNITY_EDITOR
        [BurstCompile]
#endif
        public static EIntersection RaycastTriangle(in Vector3 original, in Vector3 direction, in Vector3 p0, in Vector3 p1, in Vector3 p2, out Vector3 point)
        {
            point = Vector3.zero;
            var normal = Vector3.Cross(p1 - p0, p2 - p0);
            if (Vector3.Dot(normal, direction) >= 0)
                return EIntersection.None;
            normal = -normal.normalized;
            float dot2 = Vector3.Dot(direction, normal);
            var p = p0 - original;
            float dot1 = Vector3.Dot(normal, p);
            Vector3 oo1 = dot1 * normal;
            Vector3 oo2 = dot2 * normal;
            point = original + oo1 + (dot1 / dot2) * (direction - oo2);
            Vector3 tan;
            tan = Vector3.Cross(-normal, p1 - p0);
            if (Vector3.Dot(tan, point - p1) < 0)
                return EIntersection.Outside;
            tan = Vector3.Cross(-normal, p2 - p1);
            if (Vector3.Dot(tan, point - p2) < 0)
                return EIntersection.Outside;
            tan = Vector3.Cross(-normal, p0 - p2);
            if (Vector3.Dot(tan, point - p0) < 0)
                return EIntersection.Outside;
            return EIntersection.Inside;
        }


        //static bool ValidateRaycast(Vector3 origin, Vector3 direct, ref Vector3 point, Vector3 newPoint)
        //{
        //    if (Vector3.Dot(newPoint - origin, direct) < Vector3.Dot(point - origin, direct))
        //    {
        //        point = newPoint;
        //        return true;
        //    }
        //    return false;
        //}

#if BURST_COMPILE && !UNITY_EDITOR
        [BurstCompile]
#endif
        public static bool RaycastBox(in Vector3 origin, in Vector3 direct, in Vector3 center, in Vector3 size, out Vector3 point)
        {
            Vector3 p = Vector3.zero;
            point = p;
            var min = center - size * 0.5f;
            var max = min + size;
            // up
            if (Vector3.Dot(direct, Vector3.up) < 0
                && MathExt.CalculateIntersectionPoint(origin, direct, max, Vector3.up, out p) == EIntersection.Inside
                && p.x > min.x && p.x < max.x && p.z > min.z && p.z < max.z)
            {
                point = p;
                return true;
                //return ValidateRaycast(origin, direct, ref point, p);
            }
            // down
            if (Vector3.Dot(direct, Vector3.down) < 0
                && MathExt.CalculateIntersectionPoint(origin, direct, min, Vector3.down, out p) == EIntersection.Inside
               && p.x > min.x && p.x < max.x && p.z > min.z && p.z < max.z)
            {
                point = p;
                return true;
                //return ValidateRaycast(origin, direct, ref point, p);
            }
            // left
            if (Vector3.Dot(direct, Vector3.left) < 0
                && MathExt.CalculateIntersectionPoint(origin, direct, min, Vector3.left, out p) == EIntersection.Inside
                && p.y > min.y && p.y < max.y && p.z > min.z && p.z < max.z)
            {
                point = p;
                return true;
                //return ValidateRaycast(origin, direct, ref point, p);
            }
            // right
            if (Vector3.Dot(direct, Vector3.right) < 0
                && MathExt.CalculateIntersectionPoint(origin, direct, max, Vector3.right, out p) == EIntersection.Inside
               && p.y > min.y && p.y < max.y && p.z > min.z && p.z < max.z)
            {
                point = p;
                return true;
                //return ValidateRaycast(origin, direct, ref point, p);
            }
            // front
            if (Vector3.Dot(direct, Vector3.forward) < 0
                && MathExt.CalculateIntersectionPoint(origin, direct, max, Vector3.forward, out p) == EIntersection.Inside
                && p.x > min.x && p.x < max.x && p.y > min.y && p.y < max.y)
            {
                point = p;
                return true;
                //return ValidateRaycast(origin, direct, ref point, p);
            }
            // back
            if (Vector3.Dot(direct, Vector3.back) < 0
                && MathExt.CalculateIntersectionPoint(origin, direct, min, Vector3.back, out p) == EIntersection.Inside
                && p.x > min.x && p.x < max.x && p.y > min.y && p.y < max.y)
            {
                point = p;
                return true;
                //return ValidateRaycast(origin, direct, ref point, p);
            }
            return false;
        }

#if BURST_COMPILE && !UNITY_EDITOR
        // not complete
        [BurstCompile]
#endif
        public static bool RaycastSphere(in Vector3 origin, in Vector3 direct, in Vector3 center, in float radius, out Vector3 point)
        {
            var tan = Vector3.Cross(center - origin, direct);
            var n = Vector3.Cross(direct, tan).normalized;
            var proj = n * Vector3.Dot(origin - center, n);
            if (proj.sqrMagnitude >= radius * radius)
            {
                point = Vector3.zero;
                return false;
            }
            point = center + proj;
            return Vector3.Dot(point - origin, direct) > 0;
        }

        public static Vector3 Min(Vector3 a, Vector3 b)
        {
            return new Vector3(a.x < b.x ? a.x : b.x, a.y < b.y ? a.y : b.y, a.z < b.z ? a.z : b.z);
        }

        public static Vector3 Max(Vector3 a, Vector3 b)
        {

            return new Vector3(a.x > b.x ? a.x : b.x, a.y > b.y ? a.y : b.y, a.z > b.z ? a.z : b.z);
        }

        static bool IsIntersectMain(in Quaternion rot1, in Bounds bounds1, in Quaternion rot2, in Bounds bounds2)
        {
            var right = rot1 * Vector3.right;
            var up = rot1 * Vector3.up;
            var forward = rot1 * Vector3.forward;

            var halfSize = bounds2.size * 0.5f;

            Vector3 min, max, vert;
            Vector3 proj;

            // p0
            vert = bounds2.center + rot2 * new Vector3(-halfSize.x, -halfSize.y, -halfSize.z) - bounds1.center;
            min = new Vector3(Vector3.Dot(vert, right), Vector3.Dot(vert, up), Vector3.Dot(vert, forward));
            max = min;

            // p1
            vert = bounds2.center + rot2 * new Vector3(halfSize.x, -halfSize.y, -halfSize.z) - bounds1.center;
            proj = new Vector3(Vector3.Dot(vert, right), Vector3.Dot(vert, up), Vector3.Dot(vert, forward));
            min = Min(min, proj);
            max = Max(max, proj);

            // p2
            vert = bounds2.center + rot2 * new Vector3(halfSize.x, -halfSize.y, halfSize.z) - bounds1.center;
            proj = new Vector3(Vector3.Dot(vert, right), Vector3.Dot(vert, up), Vector3.Dot(vert, forward));
            min = Min(min, proj);
            max = Max(max, proj);

            // p3
            vert = bounds2.center + rot2 * new Vector3(-halfSize.x, -halfSize.y, halfSize.z) - bounds1.center;
            proj = new Vector3(Vector3.Dot(vert, right), Vector3.Dot(vert, up), Vector3.Dot(vert, forward));
            min = Min(min, proj);
            max = Max(max, proj);

            // p4
            vert = bounds2.center + rot2 * new Vector3(-halfSize.x, halfSize.y, -halfSize.z) - bounds1.center;
            proj = new Vector3(Vector3.Dot(vert, right), Vector3.Dot(vert, up), Vector3.Dot(vert, forward));
            min = Min(min, proj);
            max = Max(max, proj);

            // p5
            vert = bounds2.center + rot2 * new Vector3(halfSize.x, halfSize.y, -halfSize.z) - bounds1.center;
            proj = new Vector3(Vector3.Dot(vert, right), Vector3.Dot(vert, up), Vector3.Dot(vert, forward));
            min = Min(min, proj);
            max = Max(max, proj);

            // p6
            vert = bounds2.center + rot2 * new Vector3(halfSize.x, halfSize.y, halfSize.z) - bounds1.center;
            proj = new Vector3(Vector3.Dot(vert, right), Vector3.Dot(vert, up), Vector3.Dot(vert, forward));
            min = Min(min, proj);
            max = Max(max, proj);

            // p7
            vert = bounds2.center + rot2 * new Vector3(-halfSize.x, halfSize.y, halfSize.z) - bounds1.center;
            proj = new Vector3(Vector3.Dot(vert, right), Vector3.Dot(vert, up), Vector3.Dot(vert, forward));
            min = Min(min, proj);
            max = Max(max, proj);

            var size = bounds1.size * 0.5f;
            return !(min.x >= size.x || max.x <= -size.x
                || min.y >= size.y || max.y <= -size.y
                || min.z >= size.z || max.z <= -size.z);
            //    return false;
            //return true;
        }

#if BURST_COMPILE && !UNITY_EDITOR
        [BurstCompile]
#endif
        public static bool IsIntersect(in Quaternion rot1, in Bounds bounds1, in Quaternion rot2, in Bounds bounds2)
        {
            return IsIntersectMain(rot1, bounds1, rot2, bounds2) && IsIntersectMain(rot2, bounds2, rot1, bounds1);
        }

#if BURST_COMPILE && !UNITY_EDITOR
        [BurstCompile]
#endif
        public static bool IsIntersects(in Bounds a, in Bounds b)
        {
            return a.min.x < b.max.x && a.max.x > b.min.x && a.min.y < b.max.y && a.max.y > b.min.y && a.min.z < b.max.z && a.max.z > b.min.z;
        }

#if BURST_COMPILE && !UNITY_EDITOR
        [BurstCompile]
#endif
        public static bool Contains(in Bounds bounds, in Bounds sub, float errorDistance)
        {
            var min1 = bounds.min;
            var max1 = bounds.max;
            var min2 = sub.min;
            var max2 = sub.max;
            return min1.x - errorDistance <= min2.x && min1.y - errorDistance <= min2.y && min1.z - errorDistance <= min2.z
                && max1.x + errorDistance >= max2.x && max1.y + errorDistance >= max2.y && max1.z + errorDistance >= max2.z;
        }

#if BURST_COMPILE && !UNITY_EDITOR
        [BurstCompile]
#endif
        public static void ToLocalBounds(in Matrix4x4 matrix, in Bounds bounds, out Bounds localBounds)
        {
            var halfsize = bounds.size * 0.5f;
            var localCenter = bounds.center;
            Vector3 min, max, vert;
            min = matrix.MultiplyPoint(localCenter);
            max = min;

            var vec = halfsize;
            vert = matrix.MultiplyPoint(localCenter + vec);
            min = Min(min, vert);
            max = Max(max, vert);
            vert = matrix.MultiplyPoint(localCenter - vec);
            min = Min(min, vert);
            max = Max(max, vert);

            vec = new Vector3(halfsize.x, halfsize.y, -halfsize.z);

            vert = matrix.MultiplyPoint(localCenter + vec);
            min = Min(min, vert);
            max = Max(max, vert);
            vert = matrix.MultiplyPoint(localCenter - vec);
            min = Min(min, vert);
            max = Max(max, vert);

            vec = new Vector3(-halfsize.x, halfsize.y, -halfsize.z);

            vert = matrix.MultiplyPoint(localCenter + vec);
            min = Min(min, vert);
            max = Max(max, vert);
            vert = matrix.MultiplyPoint(localCenter - vec);
            min = Min(min, vert);
            max = Max(max, vert);

            vec = new Vector3(-halfsize.x, halfsize.y, halfsize.z);

            vert = matrix.MultiplyPoint(localCenter + vec);
            min = Min(min, vert);
            max = Max(max, vert);
            vert = matrix.MultiplyPoint(localCenter - vec);
            min = Min(min, vert);
            max = Max(max, vert);

            localBounds = new Bounds((min + max) * 0.5f, max - min);
        }

#if BURST_COMPILE && !UNITY_EDITOR
        [BurstCompile]
#endif
        public static bool IsPointInTriggle(in Vector3 point, in Vector3 trig0, in Vector3 trig1, in Vector3 trig2)
        {
            var normal = Vector3.Cross(trig1 - trig0, trig2 - trig0);
            Vector3 tangent;
            tangent = Vector3.Cross(normal, trig1 - trig0);
            if (Vector3.Dot(point - trig1, tangent) < 0)
                return false;
            tangent = Vector3.Cross(normal, trig2 - trig1);
            if (Vector3.Dot(point - trig2, tangent) < 0)
                return false;
            tangent = Vector3.Cross(normal, trig0 - trig2);
            if (Vector3.Dot(point - trig0, tangent) < 0)
                return false;
            return true;
        }

#if BURST_COMPILE && !UNITY_EDITOR
        [BurstCompile]
#endif
        public static bool IsDifferent(in Vector2 a, in Vector2 b, in float precision)
        {
            return Mathf.Abs(a.x - b.x) + Mathf.Abs(a.y - b.y) > precision;
        }

#if BURST_COMPILE && !UNITY_EDITOR
        [BurstCompile]
#endif
        public static bool IsDifferent(in Vector3 a, in Vector3 b, in float precision)
        {
            return Mathf.Abs(a.x - b.x) + Mathf.Abs(a.y - b.y) + Mathf.Abs(a.z - b.z) > precision;
        }

#if BURST_COMPILE && !UNITY_EDITOR
        [BurstCompile]
#endif
        public static bool IsDifferent(in Vector4 a, in Vector4 b, in float precision)
        {
            return Mathf.Abs(a.x - b.x) + Mathf.Abs(a.y - b.y) + Mathf.Abs(a.z - b.z) + Mathf.Abs(a.w - b.w) > precision;
        }

        /// <summary>
        /// 计算射线与线段的交点
        /// </summary>
        /// <param name="origin">射线原点</param>
        /// <param name="dir">射线方向</param>
        /// <param name="pointA">线段点A</param>
        /// <param name="pointB">线段点B</param>
        /// <param name="value"></param>
        /// <returns></returns>
#if BURST_COMPILE && !UNITY_EDITOR
        [BurstCompile]
#endif
        public static void CalculateIntersectionPoint(in Vector2 origin, in Vector2 dir, in Vector2 pointA, in Vector2 pointB, out bool hasPoint, out Vector2 value)
        {
            var ab = (pointB - pointA).normalized;
            var normal = new Vector2(-ab.y, ab.x);
            var proj = Vector2.Dot(origin - pointA, ab) * ab;
            var dproj = pointA + proj - origin;
            if (Vector2.Dot(dproj, dir) <= 0)
            {
                value = origin;
                hasPoint = false;
                return;
            }
            value = origin + Mathf.Abs(dproj.magnitude / Vector2.Dot(normal, dir)) * dir;
            hasPoint = Vector2.Dot(pointA - value, pointB - value) < 0;
        }

#if BURST_COMPILE && !UNITY_EDITOR
        [BurstCompile]
#endif
        public static void CalculateIntersectionPointInXOZ(in Vector3 original, in Vector3 direction, out bool hasPoint, out Vector3 point)
        {
            if (direction.y == 0)
            {
                point = original;
                hasPoint = original.y == 0;
                return;
            }
            if (direction.y * original.y > 0)
            {
                point = original;
                hasPoint = false;
                return;
            }
            float dot = Vector3.Dot(direction, Vector3.up);
            Vector3 p = original;
            p.y = 0;
            point = p - (direction - dot * Vector3.up) * original.y / dot;
            hasPoint = true;
        }

        public static Vector3 GetColliderCenter(Collider col)
        {
            if (col is BoxCollider)
            {
                var box = (BoxCollider)col;
                return box.transform.localToWorldMatrix.MultiplyPoint(box.center);
            }
            else if (col is SphereCollider)
            {
                var sph = (SphereCollider)col;
                return sph.transform.localToWorldMatrix.MultiplyPoint(sph.center);
            }
            else if (col is CapsuleCollider)
            {
                var cap = (CapsuleCollider)col;
                return cap.transform.localToWorldMatrix.MultiplyPoint(cap.center);
            }
            else
            {
                return col.transform.position;
            }
        }


#if BURST_COMPILE && !UNITY_EDITOR
        [BurstCompile]
#endif
        public static bool CalculateRectIntersection(in Rect a, in Rect b, ref Rect clip)
        {
            if (a.xMin >= b.xMax || b.xMin > a.xMax || a.yMin > b.yMax || b.yMin > a.yMax)
                return false;
            clip.xMin = Mathf.Max(a.xMin, b.xMin);
            clip.xMax = Mathf.Min(a.xMax, b.xMax);
            clip.yMin = Mathf.Max(a.yMin, b.yMin);
            clip.yMax = Mathf.Min(a.yMax, b.yMax);
            return true;
        }

        public static Vector3 GetTop(this Bounds bounds)
        {
            return new Vector3(bounds.center.x, bounds.max.y, bounds.center.z);
        }

        public static Vector3 GetBottom(this Bounds bounds)
        {
            return new Vector3(bounds.center.x, bounds.min.y, bounds.center.z);
        }

        public static Bounds CalculateLocalBounds(GameObject target, bool includeSkinnedMesh = false, bool includeInactive = false)
        {
            bool init = true;
            Bounds bounds = new Bounds(Vector3.zero, Vector3.zero);
            var meshs = target.GetComponentsInChildren<MeshFilter>(includeInactive);
            var mat = target.transform.worldToLocalMatrix;
            foreach (var mesh in meshs)
            {
                if (mesh == null || mesh.sharedMesh == null)
                    continue;
                var bd = GeometryUtility.CalculateBounds(mesh.sharedMesh.vertices, mat * mesh.transform.localToWorldMatrix);
                if (init)
                {
                    bounds = bd;
                    init = false;
                }
                else
                {
                    bounds.Encapsulate(bd);
                    //bounds.Encapsulate(bd.max);
                }
            }
            if (includeSkinnedMesh)
            {
                //var tmpMesh = new Mesh();
                var renderer = target.GetComponentsInChildren<SkinnedMeshRenderer>(includeInactive);
                foreach (var skinned in renderer)
                {
                    if (skinned == null || skinned.sharedMesh == null)
                        continue;
                    //skinned.BakeMesh(tmpMesh);
                    var bd = GeometryUtility.CalculateBounds(skinned.sharedMesh.vertices, mat * skinned.transform.localToWorldMatrix);
                    if (init)
                    {
                        bounds = bd;
                        init = false;
                    }
                    else
                    {
                        bounds.Encapsulate(bd);
                    }
                }
            }
            return bounds;
        }
#endif
    }
}